파이썬 - 변수,할당문,인터닝

Python
Author

신호연

Published

December 25, 2022

파이썬 변수,할당문,인터닝

전북대학교 최규빈 교수님의 딥러닝 deepcopy-shallowcopy 특강 중,변수와 할당문 부분을 재구성한 글입니다.

파이썬에서의 변수

  • 파이썬의 변수는 메모리상에 저장된 객체를 참조(reference)합니다.
  • 따라서 변수는 자바에서 참조변수와 같습니다.
  • 변수는 객체(object)를 부르는 별칭(다른이름),객체에 붙여진 포스트잇,또다른 레이블 이라고 생각하는 것이 비유적으로 맞습니다.
  • 앞으로 객체에 접근하기 위해서는 a라는 변수(별칭,객체)를 찾으면 됩니다.
  • 마치 C언어의 포인터와 유사하게 동작합니다.

파이썬에서의 할당문(=)

  • =은 파이썬(뿐만아니라 대부분의 프로그래밍언어)에서 할당문입니다.
  • 할당문을 실행하면 = 오른쪽에 있는 객체를 먼저 메모리 상에 생성하거나 가져옵니다.
  • =의 왼쪽에는 변수가 존재하며 파이썬은 변수에 생성된 객체를 할당합니다.
  • 변수는 객체에 붙여지는 별칭,레이블로 표현할 수 있습니다. 이렇게 객체에 변수가 할당되고나면 할당된 변수와 객체에 대해서 “변수가 객체에 바인딩(묶이다)되어있다”고 표현합니다.
a = [1,2,3]
print(id(a))
1819154486912

위 코드에서 객체[1,2,3]에 변수 a가 바인딩 되었습니다.내부동작은 다음과 같습니다.

1. 메모리 주소(1819161042880)에 리스트 객체[1,2,3]을 생성합니다
2. 생성된 리스트객체를 변수 a에 할당합니다. a는 객체[1,2,3]에 붙여지는 별칭,레이블이라고 할 수 있습니다.

에일리어싱

에일리어싱은 하나의 객체를 여러개의 변수가 참조하게 하는 것입니다. 하나의 객체에 여러개의 별칭,별명,레이블을 붙이는 것이라고도 할 수 있습니다.

b = a
print(id(a));print(id(b))
print(a);print(b)
1819154486912
1819154486912
[1, 2, 3]
[1, 2, 3]

id,value

id는 객체가 가지는 메모리상의 고유한 주소입니다. 서로다른 객체는 다른 값을 가질수도 같은값을 가질수도 있습니다.

a=[1,2,3]
b=a
a.append(4)
c=[1,2,3,4]
print(f'a:{a} b:{b} c:{c}')
print(f'id(a):{id(a)},id(b):{id(b)},id(c):{id(c)}')
a:[1, 2, 3, 4] b:[1, 2, 3, 4] c:[1, 2, 3, 4]
id(a):1819154849280,id(b):1819154849280,id(c):1819155097408

변수a,b,c는 모두 같은 value(값)을 가집니다.a와b는 같은 객체에 바인딩되어있지만 c는 또다른 객체에 바인딩되어 있습니다.

is vs ==

- is는 객체비교연산자로 두 변수가 동일한 객체를 참조하는지 아니면 다른객체를 참조하는지 확인한 후 True or False를 반환합니다. 파이썬은 내부적으로 동일한 객체인지 아닌지를 판단할때에는 메모리주소를 확인한다고 합니다.
- ==는 값비교연산자로 두 변수가 참조하는 객체의 값이 같은지 아니면 값이 다른지를 확인한 후 True or False를 반환합니다.
- 참조하다는 뭔가 잘 와닿는데 참조`라는 용어는 잘 와닿지가 않습니다…변수가 참조하는(또는 가리키는,지칭하는)객체(object) 그 자체입니다.

code1

a=[1,2,3] #1
print("append하기 전 id(a):",id(a))
b=a #2 에일리어싱,동일한 객체를 가리키도록 함.
a.append(4) #3
c=[1,2,3,4] #4
print(f'a:{a} b:{b} c:{c}')
print(f'id(a):{id(a)},id(b):{id(b)},id(c):{id(c)}')
print("a의 참조(reference)와 b의 참조는 동일한 객체인가요??",a is b)
print("a의 참조와 b의 참조는 값(value)이 같나요?",a == b)
print("a의 참조(reference)와 c의 참조는 동일한 객체인가요??",a is c)
print("a의 참조와 c의 참조는 값(value)이 같나요?",a == c)
append하기 전 id(a): 1819155097408
a:[1, 2, 3, 4] b:[1, 2, 3, 4] c:[1, 2, 3, 4]
id(a):1819155097408,id(b):1819155097408,id(c):1819155103296
a의 참조(reference)와 b의 참조는 동일한 객체인가요?? True
a의 참조와 b의 참조는 값(value)이 같나요? True
a의 참조(reference)와 c의 참조는 동일한 객체인가요?? False
a의 참조와 c의 참조는 값(value)이 같나요? True

코드설명
1. 변수a에 [1,2,3]을 할당합니다.a는 [1,2,3]을 참조합니다.
2. 에일리어싱으로 변수b도 [1,2,3]을 참조합니다.
3. 변수a가 참조하는 리스트객체[1,2,3]에 4를 추가합니다.
4. 변수c에 [1,2,3,4]를 할당합니다.

code2 - 살짝 심화

a=[1,2,3] #1
print("재할당 하기 전 id(a):",id(a))
b=a #2에일리어싱,동일한 객체를 가리키도록 함
a=[1,2,3]+[4] #3재할당
c=[1,2,3,4] #4
print(f'a:{a} b:{b} c:{c}')
print(f'id(a):{id(a)},id(b):{id(b)},id(c):{id(c)}')
print("a의 참조(reference)와 b의 참조는 동일한 객체인가요??",a is b)
print("a의 참조와 b의 참조는 값(value)이 같나요?",a == b)
print("a의 참조(reference)와 c의 참조는 동일한 객체인가요??",a is c)
print("a의 참조와 c의 참조는 값(value)이 같나요?",a == c)
재할당 하기 전 id(a): 1819155102592
a:[1, 2, 3, 4] b:[1, 2, 3] c:[1, 2, 3, 4]
id(a):1819184303168,id(b):1819155102592,id(c):1819184334272
a의 참조(reference)와 b의 참조는 동일한 객체인가요?? False
a의 참조와 b의 참조는 값(value)이 같나요? False
a의 참조(reference)와 c의 참조는 동일한 객체인가요?? False
a의 참조와 c의 참조는 값(value)이 같나요? True

코드설명
1. 변수a에 [1,2,3]을 할당합니다.a는 [1,2,3]을 참조합니다.
2. 에일리어싱으로 변수b도 [1,2,3]을 참조합니다.
3. 변수a에 리스트객체[1,2,3,4]를 재할당합니다.
4. 변수c에 [1,2,3,4]를 할당합니다.

a,b,c 각각의 값은 code1과 code2에서 모두 같습니다. 차이점은 code1에서는 a가 참조하는 리스트[1,2,3]에 4를 추가하고 code2에서는 a에 리스트[1,2,3,4]를 재할당한다는 점입니다.중요한 차이점은 다음과 같습니다.

- code1에 append전 후의 a가 참조하는 객체는 주소는 변하지 않은 것으로 보아 동일한 객체에 원소만 추가되었음을 알 수 있습니다.
- 반면 code2에서 할당문 전 후의 a가 참조하는 객체의 주소가 변합니다.
- 이전에 없었던 1)객체가 메모리에 생성되고 2)변수a는 이전의 [1,2,3]을 더 이상 참조하지 않고 생성된 객체[1,2,3,4]를 참조**하는 것을 알 수 있습니다.

인터닝

인터닝이란 이미 생성된 객체를 재사용하는 것을 말합니다. 객체의 빠른 재사용을 가능하게 하며 메모리를 절약한다고 합니다. 내부적으로는 다음과 같이 구현됩니다.
1. 임의의 할당문을 실행합니다.
2. = 오른쪽에 있는 객체가 Intern 컬렉션에 등록되어 있는지 아닌지 확인합니다.
3. 등록되어 있는 객체의 경우 그 객체를 그대로 참조합니다. 등록되지 않은 경우 메모리에 객체를 생성하며 변수는 생성된 객체의 주소를 참조합니다

자주 사용하는 객체의 경우 직접 Intern 컬렉션에 등록할 수 있고 빠르게 재사용할 수 있습니다. -5~256사이의 정수이거나 20자 미만의 문자열은 할당문을 실행하면 자동으로 Inter 컬렉션에 등록됩니다. 따라서 해당하는 정수나 문자열을 또다른 할당문에 실행하면 변수는 같은 객체를 참조합니다.

예제1-인터닝 X

a=1+2021
b=2023-1
c=2022
print(id(a),id(b),id(c))
print(a,b,c)
1819155040400 1819155039600 1819155040688
2022 2022 2022

a,b,c는 서로다른 객체를 참조하며 객체들은 모두 같은 값을 가집니다.

예제2-인터닝 O

a=1+2 
b=4-1
c=3
print(id(a),id(b),id(c))
print(a,b,c)
1819075897712 1819075897712 1819075897712
3 3 3

a,b,c는 모두같은 객체를 참조합니다. 내부적인 동작은 다음과 같습니다.
1. a=1+2 할당문을 실행합니다.
2. 할당되는 객체가 -5~256사이의 정수이므로 자동으로 Intern 컬렉션에 등록됩니다.
3. b와c에도 정수 3을 할당합니다. 3은 Intern 컬렉션 등록되어있는 객체이며 메모리상에 생성되어있는 객체이므로 새로운 3객체가 생성되지 b,c는 이미 생성된 3객체를 가리킵니다.

참고링크1 : https://guebin.github.io/DL2022/posts/Appendix/2022-12-14-A1.html
참고링크2 : http://pythonstudy.xyz/python/article/512-%ED%8C%8C%EC%9D%B4%EC%8D%AC-Object-Interning